﻿/*
 * ©2009-2010 XELF
 * http://xelf.info
 */

using System;
using System.Collections.Generic;
using System.IO;

namespace XELF.Framework {

	/// <summary>
	/// 
	/// </summary>
	public class ContentSerializerAttribute : Attribute {
		/// <summary>
		/// 
		/// </summary>
		public bool SharedResource;
	}

	/// <summary>
	/// 
	/// </summary>
	public class Texture {
		/// <summary>
		/// 名前
		/// </summary>
		public string Name;
		/// <summary>
		/// タグ
		/// </summary>
		public object Tag;
		/// <summary>
		/// テクスチャレベルの数
		/// </summary>
		public int LevelCount { get; protected set; }

		/// <summary>
		/// レベルの情報
		/// </summary>
		protected struct LevelDescription {
			/// <summary>
			/// レベルの開始インデックス
			/// </summary>
			public int LevelStart;
			/// <summary>
			/// 幅
			/// </summary>
			public int Width;
			/// <summary>
			/// 高さ
			/// </summary>
			public int Height;
			/// <summary>
			/// 深さ
			/// </summary>
			public int Depth;
			/// <summary>
			/// 要素のインデックス
			/// </summary>
			/// <param name="x"></param>
			/// <param name="y"></param>
			/// <returns></returns>
			public int this[int x, int y] {
				get {
					return x + Width * y + LevelStart;
				}
			}
			/// <summary>
			/// 要素のインデックス
			/// </summary>
			/// <param name="x"></param>
			/// <param name="y"></param>
			/// <param name="z"></param>
			/// <returns></returns>
			public int this[int x, int y, int z] {
				get {
					return x + Width * (y + Height * z) + LevelStart;
				}
			}
		}

		/// <summary>
		/// 各レベルの情報
		/// </summary>
		protected LevelDescription[] LevelDescriptions = new LevelDescription[16];

		/// <summary>
		/// レベル（ミップマップ）の情報（サイズなど）を生成する。
		/// </summary>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="depth"></param>
		/// <returns></returns>
		protected int GenerateLevelDescriptions(int width, int height, int depth) {
			int numberPixels = 0;
			for (LevelCount = 0; ; LevelCount++) {
				LevelDescriptions[LevelCount] = new LevelDescription {
					Width = width,
					Height = height,
					Depth = depth,
					LevelStart = numberPixels,
				};
				var size = width * height * depth;
				numberPixels += size;
				if (width + height <= 2) return numberPixels;
				width = Math.Max(1, width >> 1);
				height = Math.Max(1, height >> 1);
				depth = Math.Max(1, depth >> 1);
			}
		}

		protected int Address(int x, int y, int mipmapLevel) {
			return LevelDescriptions[mipmapLevel][x, y];
		}
		protected int Address(int x, int y, int z, int mipmapLevel) {
			return LevelDescriptions[mipmapLevel][x, y, z];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="rgba"></param>
		/// <returns></returns>
		protected static Vector4 RGBAToVector4(int rgba) {
			return new Vector4(
						(((uint)rgba >> 16) & 0xFF) * (1.0f / byte.MaxValue),
						(((uint)rgba >> 8) & 0xFF) * (1.0f / byte.MaxValue),
						((uint)rgba & 0xFF) * (1.0f / byte.MaxValue),
						(((uint)rgba >> 24) & 0xFF) * (1.0f / byte.MaxValue));
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="color"></param>
		/// <returns></returns>
		protected static int RGBAFrom(Vector4 color) {
			var r = (uint)MathHelper.Clamp(color.X * 255, 0, 255);
			var g = (uint)MathHelper.Clamp(color.Y * 255, 0, 255);
			var b = (uint)MathHelper.Clamp(color.Z * 255, 0, 255);
			var a = (uint)MathHelper.Clamp(color.W * 255, 0, 255);
			return unchecked((int)(
				b +
				(g << 8) +
				(r << 16) +
				(a << 24)));
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public sealed class Texture2D : Texture {
		/// <summary>
		/// 幅 [ピクセル数]
		/// </summary>
		public int Width { get; private set; }
		/// <summary>
		/// 高さ [ピクセル数]
		/// </summary>
		public int Height { get; private set; }

		int[] pixels;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="pixels"></param>
		public void SetData(int[] pixels) {
			Array.Copy(pixels, this.pixels, pixels.Length);

			GenerateMipMaps();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="pixels"></param>
		public void GetData(int[] pixels) {
			Array.Copy(this.pixels, pixels, pixels.Length);
		}

		void GenerateMipMaps() {
			for (int m1 = 1; m1 < LevelCount; m1++) {
				var m0 = m1 - 1;
				var w0 = LevelDescriptions[m0].Width;
				var h0 = LevelDescriptions[m0].Height;
				var w1 = LevelDescriptions[m1].Width;
				var h1 = LevelDescriptions[m1].Height;
				var sx = 1.0f / w0 * ((float)w0 / w1);
				var sy = 1.0f / h0 * ((float)h0 / h1);
				for (int y1 = 0; y1 < h1; y1++) {
					for (int x1 = 0; x1 < w1; x1++) {
						pixels[Address(x1, y1, m1)] = RGBAFrom(this[(x1 + 0.5f) * sx, (y1 + 0.5f) * sy, m0]);
					}
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="width"></param>
		/// <param name="height"></param>
		public Texture2D(int width, int height) {
			Width = width;
			Height = height;

			var numberPixels = GenerateLevelDescriptions(width, height, 1);
			pixels = new int[numberPixels];
		}

		internal int this[int x, int y, int mipmapLevel] {
			get {
				return pixels[Address(x, y, mipmapLevel)];
			}
		}

		/// <summary>
		/// 双線形補間。
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="mipMapLevel"></param>
		/// <returns></returns>
		internal Vector4 this[float x, float y, int mipMapLevel] {
			get {
				var width = LevelDescriptions[mipMapLevel].Width;
				var height = LevelDescriptions[mipMapLevel].Height;
				// Wrap

				x -= 0.5f / width;
				y -= 0.5f / height;

				x = x - MathHelper.Floor(x);
				y = y - MathHelper.Floor(y);

				x = x * width;
				y = y * height;
				var x0 = (int)x % width; // Wrap
				var y0 = (int)y % height; // Wrap

				// Point
				//return RGBAToVector4(Texture[ix, iy]);

				// Linear
				x -= x0;
				y -= y0;
				int x1 = (x0 + 1) % width;
				int y1 = (y0 + 1) % height;
				Vector4 a = RGBAToVector4(this[x0, y0, mipMapLevel]);
				Vector4 b = RGBAToVector4(this[x1, y0, mipMapLevel]);
				Vector4 c = RGBAToVector4(this[x0, y1, mipMapLevel]);
				Vector4 d = RGBAToVector4(this[x1, y1, mipMapLevel]);
				Vector4 result;
				Vector4.Bilinear(ref a, ref b, ref c, ref d, x, y, out result);
				return result;
			}
		}

		/// <summary>
		/// 三重線形補間。
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="mipMapLevel"></param>
		/// <returns></returns>
		internal Vector4 this[float x, float y, float mipMapLevel] {
			get {
				mipMapLevel = MathHelper.Clamp(mipMapLevel, 0, LevelCount - 1);
				var m = (int)mipMapLevel;

				var a = this[x, y, m];
				var b = this[x, y, MathHelper.Clamp(m + 1, 0, LevelCount - 1)];
				Vector4 result;
				Vector4.Lerp(ref a, ref b, mipMapLevel - m, out result);
				return result;
			}
		}
	}

	/// <summary>
	/// 
	/// </summary>
	public class Sampler {
		/// <summary>
		/// 
		/// </summary>
		public Texture2D Texture;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="position"></param>
		/// <returns></returns>
		public Vector4 this[Vector2 position] {
			get {
				return this[position.X, position.Y, 0];
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="position"></param>
		/// <returns></returns>
		public Vector4 this[Vector4 position] {
			get {
				return this[position.X, position.Y, position.W];
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="mipmapLevel"></param>
		/// <returns></returns>
		public Vector4 this[float x, float y, float mipmapLevel] {
			get {
				if (Texture == null) return Vector4.Zero;
				return Texture[x, y, mipmapLevel];
			}
		}

	}

	/// <summary>
	/// 
	/// </summary>
	public class GraphicsDevice {
	}

	/// <summary>
	/// 
	/// </summary>
	public interface ContentLoader {
		/// <summary>
		/// 
		/// </summary>
		/// <param name="type"></param>
		/// <param name="name"></param>
		/// <param name="content"></param>
		/// <returns></returns>
		bool Load(Type type, string name, out object content);
	}

	/// <summary>
	/// 
	/// </summary>
	public class ContentManager : IDisposable {
		/// <summary>
		/// 
		/// </summary>
		public ContentManager() {
			rootDirectory = string.Empty;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="rootDirectory"></param>
		public ContentManager(string rootDirectory) {
			this.rootDirectory = rootDirectory;
		}
		/// <summary>
		/// 
		/// </summary>
		public List<ContentLoader> Loaders = new List<ContentLoader>();

		string rootDirectory;
		/// <summary>
		/// 
		/// </summary>
		public string RootDirectory { get { return rootDirectory; } set { rootDirectory = value; } }

		readonly Dictionary<string, object> contents = new Dictionary<string, object>();

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="name"></param>
		/// <returns></returns>
		public T Load<T>(string name) {
			object content;
			if (contents.TryGetValue(name, out content)) return (T)content;

			string path = Path.Combine(RootDirectory, name);
			foreach (var loader in Loaders) {
				if (loader.Load(typeof(T), path, out content)) {
					contents.Add(name, content);
					return (T)content;
				}
			}
			throw new Exception(name);
		}
		/// <summary>
		/// 
		/// </summary>
		public void Unload() {
			foreach (var content in contents.Values) {
				var disposable = content as IDisposable;
				if (disposable != null) disposable.Dispose();
			}
			contents.Clear();
		}
		/// <summary>
		/// 
		/// </summary>
		public void Dispose() {
			Unload();
		}
	}

}
